﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Text.RegularExpressions;
using System.Windows.Media.Media3D;
using System.Globalization;

namespace RLProject1.Model.Utility
{
    public static class MeshSerializer
    {
        /// <summary>
        /// Findet f, v, vn (Gruppe 1) und die restliche Zeile (Gruppe 2)
        /// </summary>
        const string REGEX_LINE = @"^(\w+)\b(.*)";
        /// <summary>findet [-]n.nnnnn. Für v und vn-Zeilen</summary>
        const string REGEX_V = @"-?\d+\.\d+(e-\d+)?";
        /// <summary>findet VertexIdx//NormalIdx. Für f-Zeilen. Zahlen in Gruppe 1 und 2.</summary>
        const string REGEX_F = @"(\d+)//(\d+)";
        
        public static void LoadFromObj(this Mesh mesh, string path)
        {
            Regex rx_line = new Regex(MeshSerializer.REGEX_LINE);
            Regex rx_v = new Regex(MeshSerializer.REGEX_V); //v oder vn
            Regex rx_f = new Regex(MeshSerializer.REGEX_F); // f
            try
            {
                using (StreamReader reader = new StreamReader(path))
                {
                    string[] lineBuffer = new string[2];
                    string line;
                    int counter = 0;
                    bool moreLines;
                    do
                    {
                        line = reader.ReadLine();
                        moreLines = (line != null);
                        if (moreLines && counter < lineBuffer.Length)
                        {
                            lineBuffer[counter] = line;
                            counter++;
                            continue; // nächste Zeile in Buffer laden
                        }
                        // --- lineBuffer abarbeiten ---
                        for (int j = 0; j < counter; j++)
                        {
                            line = lineBuffer[j];
                            // Suche nach Daten-Zeile (v,vn,f irgendwas):
                            Match lineMatch = rx_line.Match(line);
                            if (lineMatch.Success)
                            { // 
                                switch (lineMatch.Groups[1].Value)
                                {
                                    case "f": // TriangleIndizes
                                        MatchCollection fMatches = rx_f.Matches(lineMatch.Groups[2].Value);
                                        if (fMatches.Count != 3)
                                        { // 3 Zahlengruppen je Fläche erwartet!
                                            break;
                                        }
                                        int[] indizes = new int[3];
                                        for (int i = 0; i < 3; i++)
                                        { //Note: erste und zweite Zahl sollten gleich sein -> nimm nur ersten Index
                                            indizes[i] = Int32.Parse(fMatches[i].Groups[1].Value);
                                        }
                                        mesh.AddTriangleIndexes(indizes[0], indizes[1], indizes[2]);
                                        break;
                                    case "v":
                                    // erstmal gleich wie "vn":
                                    case "vn":
                                        MatchCollection vMatches = rx_v.Matches(lineMatch.Groups[2].Value);
                                        if (vMatches.Count != 3)
                                        { // 3 Zahlengruppen je Fläche erwartet!
                                            break;
                                        }
                                        Vector3D vector = new Vector3D();
                                        try
                                        {
                                            vector.X = Double.Parse(vMatches[0].Groups[0].Value, NumberStyles.Float, NumberFormatInfo.InvariantInfo);
                                            vector.Y = Double.Parse(vMatches[1].Groups[0].Value, NumberStyles.Float, NumberFormatInfo.InvariantInfo);
                                            vector.Z = Double.Parse(vMatches[2].Groups[0].Value, NumberStyles.Float, NumberFormatInfo.InvariantInfo);
                                        }
                                        catch (Exception ex)
                                        {
                                            Debug.Fail("Fehler beim parsen von v/vn Wert", ex.Message);
                                            break; ;
                                        }
                                        if ("vn" == lineMatch.Groups[1].Value)
                                        { // case "vn"
                                            mesh.AddNormal(vector);
                                            break;
                                        }
                                        mesh.AddVertex(vector); // case "v"
                                        break;
                                    default:
                                        break;
                                }
                            } // end if (lineMatch.Success)
                        }
                        counter = 0;
                    } while (moreLines);
                } 
            }
            catch (Exception e)
            {
                Debug.Fail("Datei kann nicht geladen werden", e.Message);
            }
        }


    }
}
